import paddle
import paddle.nn as nn
import paddle.nn.functional as F
import numpy as np
import pandas as pd
from sklearn.model_selection import train_test_split
from sklearn.preprocessing import MinMaxScaler
from sklearn.metrics import accuracy_score, f1_score, roc_auc_score, classification_report
from sklearn.impute import SimpleImputer  # 导入插补器
from imblearn.over_sampling import SMOTE  # 导入SMOTE
import matplotlib.pyplot as plt


# 确保PaddlePaddle处于动态图模式
paddle.disable_static()

# 加载数据集
data = pd.read_excel('E:\\2022040424 何欣 综合实践2\\11.23\data\糖尿病预测数据集.xlsx')

# 定义特征和标签
features = data.drop('糖尿病', axis=1).values
labels = data['糖尿病'].values

# 分割数据集为训练集和测试集
train_features, test_features, train_labels, test_labels = train_test_split(features, labels, test_size=0.2,
                                                                            random_state=42)

# 特征归一化
scaler = MinMaxScaler()
train_features_scaled = scaler.fit_transform(train_features)
test_features_scaled = scaler.transform(test_features)

# 使用SimpleImputer插补训练集中的缺失值
imputer = SimpleImputer(missing_values=np.nan, strategy='mean')
train_features_scaled = imputer.fit_transform(train_features_scaled)

# 过采样
smote = SMOTE(random_state=42)
train_features_smote, train_labels_smote = smote.fit_resample(train_features_scaled, train_labels)

# 将过采样后的数据转换为paddle tensor
train_features_smote = paddle.to_tensor(train_features_smote, dtype='float32')
test_features_scaled = paddle.to_tensor(test_features_scaled, dtype='float32')
train_labels_smote = paddle.to_tensor(train_labels_smote.astype('float32'), dtype='float32').unsqueeze(1)
test_labels = paddle.to_tensor(test_labels.astype('float32'), dtype='float32').unsqueeze(1)


# 构建深度神经网络
class DNN(nn.Layer):
    def __init__(self):
        super(DNN, self).__init__()
        self.fc1 = nn.Linear(8, 64)  # 输入层到隐藏层
        self.fc2 = nn.Linear(64, 32)  # 隐藏层到隐藏层
        self.fc3 = nn.Linear(32, 1)  # 隐藏层到输出层

    def forward(self, x):
        x = F.relu(self.fc1(x))
        x = F.relu(self.fc2(x))
        x = F.sigmoid(self.fc3(x))  # 使用sigmoid激活函数
        return x


# 初始化模型
model = DNN()

# 编译模型
optimizer = paddle.optimizer.Adam(parameters=model.parameters(), learning_rate=0.001)
loss_fn = nn.BCEWithLogitsLoss()




# 绘制特征重要性并保存图片
with paddle.no_grad():
    weights = model.fc1.weight.numpy()
    feature_importance = np.sum(np.abs(weights), axis=1)
    plt.figure(figsize=(10, 6))
    plt.bar(range(len(feature_importance)), feature_importance)
    plt.xlabel('Features')
    plt.ylabel('Importance')
    plt.title('Feature Importance')
    plt.savefig('E:\\2022040424 何欣 综合实践2\\11.23\\ui_images\diabetes.png')  # 保存图片到image文件夹下
    plt.show()


# 预测函数，包括自定义阈值
def predict1(model, features, threshold=0.5):
    features_scaled = scaler.transform(features)
    features_scaled = paddle.to_tensor(features_scaled, dtype='float32')

    model.eval()
    with paddle.no_grad():
        predictions = model(features_scaled)
        predictions = (predictions > threshold).astype('int32')

    return predictions.numpy()

# 训练模型
def train_model(model, train_features, train_labels, epochs=100):
    for epoch in range(epochs):
        # 前向传播
        logits = model(train_features)
        loss = loss_fn(logits, train_labels)
        # 反向传播
        loss.backward()
        optimizer.step()
        optimizer.clear_grad()
    return model

# 训练模型
model = train_model(model, train_features_smote, train_labels_smote)




def predict(model, features_tensor, threshold=0.5):
    # 确保 features_tensor 是一个二维数组
    if features_tensor.ndim == 1:
        features_tensor = features_tensor.reshape(1, -1)
    elif features_tensor.ndim > 2:
        raise ValueError("features_tensor 必须是一个二维数组")

    # 确保 features_tensor 是一个 paddle.Tensor 对象
    assert isinstance(features_tensor, paddle.Tensor), "features_tensor 必须是一个 paddle.Tensor 对象"

    # 特征归一化
    # 将 paddle.Tensor 转换为 numpy 数组
    features_np = features_tensor.numpy()
    features_scaled = scaler.transform(features_np)  # 使用 NumPy 数组进行归一化

    # 将归一化后的 NumPy 数组转换回 PaddlePaddle Tensor
    features_scaled = paddle.to_tensor(features_scaled, dtype='float32')

    # 使用模型进行预测
    model.eval()
    with paddle.no_grad():
        predictions = model(features_scaled)
        predictions = (predictions > threshold).astype('int32')

    return predictions.numpy()  # 将预测结果转换回 NumPy 数组

paddle.save(model.state_dict(), 'E:\\2022040424 何欣 综合实践2\\11.23\\model\\diabetes_model.pdparams')

# 加载模型状态字典
def load_model():
    model = DNN()
    model_state = paddle.load('E:\\2022040424 何欣 综合实践2\\11.23\\model\\diabetes_model.pdparams')
    model.set_state_dict(model_state)
    model.eval()
    return model